/**
 *
 */
package siebes.rmi.sorter;

import java.util.Observable;
import java.util.Observer;

/**
 * @author eddie
 *
 */
public class ListJoiner implements Observer  {

    /**
     * gejoined lijst.
     */
    @SuppressWarnings("rawtypes")
    private static Comparable[] joinedList;

    /**
     * container voor de te samenvoegen lijsten.
     */
    @SuppressWarnings("rawtypes")
    private Comparable[][] unjoinedList;

    /**
     * Aantal arrays die al gesorteerd zijn.
     */
    private int returnedNumbers = 0;

    /**
     * Totaal aantal arrays die aan het sorteren zijn.
     */
    private final int totalNumbers;


    /**
     * Tijdmeting.
     */
    private Stopwatch stopwatch;

    /**
     * Joiner van lijsten.
     * @param numberOfProcesses aantal processen die voor lijsten draaien.
     */
    public ListJoiner(final int numberOfProcesses) {
        joinedList = new Comparable[0];
        unjoinedList = new Comparable[numberOfProcesses][];
        totalNumbers = numberOfProcesses;
        stopwatch = new Stopwatch();
        stopwatch.start();
    }

    /**
     * Voegt comparable sorteerde lijst samen in
     * juiste volgorde.
     * @param comparable te joinen array
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public final void join(final Comparable[] comparable) {
        Comparable[] tempComparable = joinedList;
        if (tempComparable.length > 0) {
            if (comparable[0].compareTo(tempComparable[0]) < 0) {
                joinedList = concat(comparable, tempComparable);
            } else {
                joinedList = concat(tempComparable, comparable);
            }
        } else {
            joinedList = comparable;
        }
    }

    /**
     * Print de lijst.
     */
    @SuppressWarnings("rawtypes")
    public final void printJoinedList() {
        for (Comparable item : joinedList) {
            System.out.println(item);
        }
    }

    /**
     * voeg 2 arrays samen.
     * @param a array 1
     * @param b array 2
     * @param <T> t comparable
     * @return samengevoegde array
     */
     private static <T> T[] concat(final T[] a, final T[] b) {
        final int alen = a.length;
        final int blen = b.length;
        if (alen == 0) {
            return b;
        }
        if (blen == 0) {
            return a;
        }
        @SuppressWarnings("unchecked")
        final T[] result = (T[]) java.lang.reflect.Array.
                newInstance(a.getClass().getComponentType(), alen + blen);
        System.arraycopy(a, 0, result, 0, alen);
        System.arraycopy(b, 0, result, alen, blen);
        return result;
    }

    /* (non-Javadoc)
     * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    @Override
    public final void update(final Observable o, final Object arg) {
        SorterThread sorterThread = (SorterThread) o;
        unjoinedList[sorterThread.getThreadNumber()] = (Comparable[]) arg;
        returnedNumbers++;
        if (returnedNumbers == totalNumbers) {
            joinAll();
            stopwatch.stop();
            printJoinedList(); //print het resultaat.
            String infoLogging = "Integer," + totalNumbers + ",";
            infoLogging += joinedList.length + "," + stopwatch.getElapsedTime();
            FileAppender.appendLogging("sorterLog.txt", infoLogging);
        }
    }

    /**
     * Functie om snel van unjoinedlist de  joinedlist te maken.
     */
    @SuppressWarnings("rawtypes")
    private void joinAll() {
        for (Comparable[] comp : unjoinedList) {
            join(comp);
        }
    }

}
